package com.hoshiicloud.payment.service.impl;

import com.alibaba.fastjson.JSON;
import com.hoshiicloud.common.rpc.Response;
import com.hoshiicloud.payment.config.PayConfig;
import com.hoshiicloud.payment.config.WeixinConfig;
import com.hoshiicloud.payment.enumeration.TradeStatus;
import com.hoshiicloud.payment.param.PayRequest;
import com.hoshiicloud.payment.param.RefundRequest;
import com.hoshiicloud.payment.vo.PayBusinessResponse;
import com.hoshiicloud.payment.vo.QueryOrderBusinessResponse;
import com.hoshiicloud.payment.vo.RefundBusinessResponse;
import com.hoshiicloud.payment.sdk.wxpay.WXPay;
import com.hoshiicloud.payment.sdk.wxpay.WXPayUtil;
import com.hoshiicloud.payment.service.WeixinPayService;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class WeixinPayServiceImpl implements WeixinPayService {

    /**
     * @param payRequest
     * @param weixinConfig
     * @return com.hoshiicloud.common.rpc.Response
     * @author: Mr.wu
     * @descripton: 微信支付统一下单接口, 接入商户需提前上传证书至服务器，否则支付失败
     * @date: 17:25 2019/5/16
     */
    @Override
    public Response order(PayRequest payRequest, WeixinConfig weixinConfig) {
        try {
            Map<String, String> data = payRequest.WeixinRequestData();
            WXPay wxPay = new WXPay(weixinConfig, PayConfig.getWeixinPayNotity(), true,
                    PayConfig.debug);
            log.info("======支付回调地址："+PayConfig.getWeixinPayNotity()+"==================");
            //data.put("notify_url", PayConfig.getWeixinPayNotity());
            Map<String, String> result = wxPay.unifiedOrder(data);
            log.info(result.toString());
            if ("SUCCESS".equals(result.get("return_code")) && "SUCCESS"
                    .equals(result.get("result_code"))) {
                //签名验证
                if ("SUCCESS".equals(result.get("result_code"))) {
                    String prepayID = result.get("prepay_id");
                    //判断交易类型
                    String method = "";
                    String payData = "";
                    String tradeType = result.get("trade_type");
                    switch (tradeType) {
                        case "NATIVE":
                            method = "qrcode";
                            payData = result.get("code_url");
                            break;
                        case "JSAPI": {
                            method = "param";
                            //组装参数
                            Map<String, String> resultData = new HashMap<>(16);
                            resultData.put("appId", result.get("appid"));
                            resultData.put("timeStamp",
                                    String.valueOf(Instant.now().getEpochSecond()));
                            resultData.put("nonceStr", RandomStringUtils.randomAlphanumeric(32));
                            resultData.put("package", "prepay_id=" + result.get("prepay_id"));
                            resultData.put("signType", wxPay.getSignTypeStr());
                            log.info("签名参数:" + resultData.toString());
                            String sign = WXPayUtil
                                    .generateSignature(resultData, weixinConfig.getKey(),
                                            wxPay.getSignType());
                            log.info("sign:" + sign);
                            resultData.put("paySign", sign);
                            payData = JSON.toJSONString(resultData);
                            break;
                        }
                        case "MWEB":
                            method = "url";
                            payData = result.get("mweb_url");
                            break;
                        case "NATIVEAPP": {
                            method = "param";
                            //组装参数
                            Map<String, String> resultData = new HashMap<>(16);
                            resultData.put("appid", result.get("appid"));
                            resultData.put("partnerid", result.get("mch_id"));
                            resultData.put("timestamp",
                                    String.valueOf(LocalDateTime.now().getSecond()));
                            resultData.put("noncestr", RandomStringUtils.random(32));
                            resultData.put("prepayid", result.get("prepay_id"));
                            resultData.put("package", "Sign=WXPay");
                            log.info("签名参数:" + resultData.toString());
                            String sign = WXPayUtil
                                    .generateSignature(resultData, weixinConfig.getKey(),
                                            wxPay.getSignType());
                            log.info("sign:" + sign);
                            resultData.put("sign", sign);
                            resultData.put("signType", wxPay.getSignTypeStr());
                            payData = JSON.toJSONString(resultData);
                            break;
                        }
                        default:
                            break;
                    }
                    PayBusinessResponse PayBusinessResponse = new PayBusinessResponse(method,
                            payData);
                    PayBusinessResponse.setSellerId(weixinConfig.getAppID());

                    return Response.data(PayBusinessResponse);
                } else {
                    //签名失败
                    return Response.failed("微信报文签名失败");
                }
            } else {
                //交易失败
                log.error("微信交易失败:" + result.toString());
                if (PayConfig.processDebug && PayConfig.debug) {
                    //测试流程,交易失败仍返回数据以进行流程
                    PayBusinessResponse PayBusinessResponse = new PayBusinessResponse("test",
                            "testData");
                    PayBusinessResponse.setSellerId(weixinConfig.getAppID());
                    return Response.success(result.toString(), PayBusinessResponse);
                }
                String errMsg = StringUtils.isNotBlank(result.get("err_code_des")) ? result
                        .get("err_code_des") : result.get("return_msg");
                return Response.failed(errMsg);
            }
        } catch (Exception e) {
            log.error("微信支付异常:", e);
            return Response.failed("微信支付异常:" + e.getMessage());
        }
    }

    /**
     * @param merOrderId
     * @param weixinConfig
     * @return com.hoshiicloud.common.rpc.Response
     * @author: Mr.wu
     * @descripton: 微信支付查询订单
     * @date: 19:34 2019/5/16
     */
    @Override
    public Response queryOrder(String merOrderId, WeixinConfig weixinConfig) {
        try {
            WXPay wxPay = new WXPay(weixinConfig, true, PayConfig.debug);

            Map<String, String> data = new HashMap<String, String>();
            data.put("out_trade_no", merOrderId);
            Map<String, String> result = wxPay.orderQuery(data);
            log.info("接口返回内容:" + result);
            if ("SUCCESS".equals(result.get("return_code"))) {
                if (wxPay.isResponseSignatureValid(result)) {
                    QueryOrderBusinessResponse response = new QueryOrderBusinessResponse();
                    if ("SUCCESS".equals(result.get("result_code"))) {
                        //业务结果成功
                        //转换返回类型,控制层统一处理订单信息
                        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
                        response.setTradeNo(result.get("out_trade_no"));
                        TradeStatus tradeStatus = null;
                        switch (result.get("trade_state")) {
                            case "SUCCESS":
                                //金额从分转为元
                                response.setTotalAmount(new BigDecimal(result.get("total_fee"))
                                        .divide(new BigDecimal(100))
                                        .setScale(2, RoundingMode.DOWN));
                                response.setPayAmount(new BigDecimal(result.get("cash_fee"))
                                        .divide(new BigDecimal(100)
                                                .setScale(2, RoundingMode.DOWN)));
                                response.setBuyerAccount(result.get("openid"));
                                response.setOutTradeNo(result.get("transaction_id"));
                                response.setPayTime(
                                        LocalDateTime.parse(result.get("time_end"), df));
                                tradeStatus = TradeStatus.SUCCESS;
                                break;
                            case "REFUND":
                                tradeStatus = TradeStatus.REFUND;
                                break;
                            case "NOTPAY":
                            case "REVOKED":
                            case "USERPAYING":
                                //REVOKED—已撤销（付款码支付）和USERPAYING--用户支付中（付款码支付）当作未支付处理
                                tradeStatus = TradeStatus.NOTPAY;
                                break;
                            case "CLOSED":
                                tradeStatus = TradeStatus.CLOSED;
                                break;
                            case "PAYERROR":
                                tradeStatus = TradeStatus.PAYERROR;
                                break;
                            default:
                                break;
                        }
                        response.setTradeStatus(tradeStatus);
                        return Response.data(response);
                    } else {
                        //业务结果失败
                        response.setTradeStatus(TradeStatus.PAYERROR);
                        return Response.data(response);
                    }
                } else {
                    //签名失败
                    return Response.failed("微信报文签名失败");
                }
            } else {
                //交易失败
                log.error("微信查询订单失败:" + result.toString());
                return Response.failed(result.get("return_msg"));
            }
        } catch (Exception e) {
            log.error("微信查询订单异常" + e.toString());
            return Response.failed("微信查询订单异常" + e.toString());
        }
    }

    /**
     * @param refundNo 退款单号
     * @param weixinConfig
     * @return com.hoshiicloud.common.rpc.Response
     * @author: Mr.wu
     * @descripton: 微信支付查询退款订单
     * @date: 15:50 2019/6/11
     */
    @Override
    public Response queryRefundOrder(String refundNo, WeixinConfig weixinConfig) {
        try {
            WXPay wxPay = new WXPay(weixinConfig, true, PayConfig.debug);

            Map<String, String> data = new HashMap<String, String>();
            data.put("out_refund_no", refundNo);
            log.info("接口请求参数:" + data.toString());
            Map<String, String> result = wxPay.refundQuery(data);
            log.info("接口返回内容:" + result);
            if ("SUCCESS".equals(result.get("return_code"))) {
                if (wxPay.isResponseSignatureValid(result)) {
                    QueryOrderBusinessResponse response = new QueryOrderBusinessResponse();
                    if ("SUCCESS".equals(result.get("result_code"))) {
                        //业务结果成功
                        //转换返回类型,控制层统一处理订单信息
                        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                        response.setTradeNo(result.get("out_refund_no_0"));
                        TradeStatus tradeStatus = null;
                        //因为此处用退款单号查询，每次应只返回单条记录,所以下标固定0
                        switch (result.get("refund_status_0")) {
                            case "SUCCESS":
                                //退款成功
                                response.setTotalAmount(new BigDecimal(result.get("total_fee"))
                                        .divide(new BigDecimal(100))
                                        .setScale(2, RoundingMode.DOWN));
                                response.setPayAmount(new BigDecimal(result.get("refund_fee_0"))
                                        .divide(new BigDecimal(100)
                                                .setScale(2, RoundingMode.DOWN)));
                                response.setBuyerAccount(result.get("refund_recv_accout_0"));
                                response.setOutTradeNo(result.get("refund_id_0"));
                                response.setPayTime(LocalDateTime
                                        .parse(result.get("refund_success_time_0"), df));
                                tradeStatus = TradeStatus.SUCCESS;
                                break;
                            case "PROCESSING":
                                //处理中
                                tradeStatus = TradeStatus.PROCESSING;
                                break;
                            case "REFUNDCLOSE":
                                //退款关闭
                                tradeStatus = TradeStatus.CLOSED;
                                break;
                            case "CHANGE":
                                //退款异常
                                tradeStatus = TradeStatus.PAYERROR;
                                break;
                            default:
                                break;
                        }
                        response.setTradeStatus(tradeStatus);
                        return Response.data(response);
                    } else {
                        //业务结果失败
                        response.setTradeStatus(TradeStatus.PAYERROR);
                        return Response.data(response);
                    }

                } else {
                    //签名失败
                    return Response.failed("微信报文签名失败");
                }
            } else {
                //交易失败
                log.error("微信查询退款订单失败:" + result.toString());
                return Response.failed(result.get("return_msg"));
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("微信查询退款订单异常" + e.toString());
            return Response.failed("微信查询退款订单异常" + e.toString());
        }
    }

    /**
     * @param merOrderId
     * @param weixinConfig
     * @return com.hoshiicloud.common.rpc.Response
     * @author: Mr.wu
     * @descripton: 微信支付关闭订单
     * @date: 17:36 2019/5/17
     */
    @Override
    public Response closeOrder(String merOrderId, WeixinConfig weixinConfig) {
        try {
            WXPay wxPay = new WXPay(weixinConfig, true, PayConfig.debug);
            Map<String, String> data = new HashMap<>(16);
            data.put("out_trade_no", merOrderId);
            Map<String, String> result = wxPay.closeOrder(data);
            log.info("微信退款接口返回信息:"+result.toString());
            if ("SUCCESS".equals(result.get("return_code"))) {
                if (wxPay.isResponseSignatureValid(result)) {
                    //转换返回类型,控制层统一处理
                    if ("SUCCESS".equals(result.get("result_code"))) {
                        return Response.success(result.get("result_msg"));
                    } else {
                        return Response.failed(result.get("err_code_des"));
                    }
                } else {
                    //签名失败
                    return Response.failed("微信报文签名失败");
                }
            } else {
                //交易失败
                log.error("微信关闭订单失败:" + result.toString());
                return Response.failed(result.get("return_msg"));
            }
        } catch (Exception e) {
            log.error("微信关闭订单异常" + e.getMessage());
            return Response.failed("微信关闭订单异常" + e.getMessage());
        }
    }

    /**
     * @param param
     * @param weixinConfig
     * @return com.hoshiicloud.common.rpc.Response
     * @author: Mr.wu
     * @descripton: 微信支付退款 接口需要双向证书
     * @date: 11:02 2019/5/18
     */
    @Override
    public Response refund(RefundRequest param, WeixinConfig weixinConfig) {
        try {
            WXPay wxPay = new WXPay(weixinConfig, PayConfig.getWeixinRefundNotity(), true,
                    PayConfig.debug);
            Map<String, String> data = new HashMap<String, String>();
            data.put("out_trade_no", param.getMerOrderId());
            data.put("out_refund_no", param.getRefundNo());
            data.put("total_fee", param.getTotalAmount().toString());
            data.put("refund_fee", param.getRefundAmount().toString());
            data.put("refund_desc", param.getRefundDesc());
            data.put("notify_url", PayConfig.getWeixinRefundNotity());
            log.info("接口请求参数:" + data.toString());
            Map<String, String> result = wxPay.refund(data);
            log.info("退款返回内容:" + result.toString());
            if ("SUCCESS".equals(result.get("return_code"))) {
                if (wxPay.isResponseSignatureValid(result)) {
                    //转换返回类型,控制层统一处理
                    if ("SUCCESS".equals(result.get("result_code"))) {
                        RefundBusinessResponse refundBusinessResponse = new RefundBusinessResponse();
                        refundBusinessResponse.setTradeNo(result.get("out_trade_no"));
                        refundBusinessResponse.setOutTradeNo(result.get("transaction_id"));
                        refundBusinessResponse.setRefundNo(result.get("out_refund_no"));
                        refundBusinessResponse.setOutRefundNo(result.get("refund_id"));
                        refundBusinessResponse.setTotalAmount(
                                new BigDecimal(result.get("total_fee")).divide(new BigDecimal(100))
                                        .setScale(2, RoundingMode.DOWN));
                        refundBusinessResponse.setRefundAmount(
                                new BigDecimal(result.get("refund_fee")).divide(new BigDecimal(100))
                                        .setScale(2, RoundingMode.DOWN));
                        return Response.data(refundBusinessResponse);
                    } else {
                        return Response.failed("微信返回："+result.get("err_code_des"));
                    }
                } else {
                    //签名失败
                    return Response.failed("微信报文签名失败");
                }
            } else {
                //交易失败
                log.error("微信退款失败:" + result.toString());
                return Response.failed(result.get("return_msg"));
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("微信退款异常" + e.getMessage());
            return Response.failed("微信退款异常" + e.getMessage());
        }
    }
}
